#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <assert.h>

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <libavutil/pixdesc.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>

int saveAsYuv420p(AVFrame *pFrame, int index) {
	int file = open("output.yuv", O_WRONLY | O_CREAT | O_TRUNC);
//	AV_PIX_FMT_YUV420P
	printf("pFrame->format = %d\n", pFrame->format);
	assert(pFrame->format == AV_PIX_FMT_YUV420P);
	//save y
	size_t t = write(file, pFrame->data[0], pFrame->width * pFrame->height);
	if (t <= 0) {
		printf("<0\n");
	} else {
		printf("write ok   \n");
	}

	//save u
	t = write(file, pFrame->data[1], pFrame->width * pFrame->height / 4);
	if (t <= 0) {
		printf("<0\n");
	} else {
		printf("write ok   \n");
	}

	//save v
	t = write(file, pFrame->data[2], pFrame->width * pFrame->height / 4);
	if (t <= 0) {
		printf("<0\n");
	} else {
		printf("write ok   \n");
	}
	close(file);
	return 0;
}

/**
 *
 */
int main(int argc, char **argv) {
	int ret;
	const char *inFileName = "../missu.mp4";
	AVFormatContext *ifmt_ctx = NULL;
	AVPacket *packet = NULL;
	packet = av_packet_alloc();
	AVFrame *pFrame = NULL;
	AVCodecContext *decode_ctx;

	if (avformat_open_input(&ifmt_ctx, inFileName, NULL, NULL) != 0) {
		perror("avformat_open_input");
		return -1;
	}

	if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
		perror("avformat_find_stream_info");
		fprintf(stdout, "Couldn't find stream information \n");
		return -1;
	}
	if (ifmt_ctx->nb_streams == 0) {
		printf("nb_streams is 0,exit \n");
		return -1;
	}

	// Find the first video stream
	int videoStream = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1,
	NULL, 0);

	if (videoStream == -1) {
		printf("Didn't find a video stream  \n");
		return -1;
	}

	AVStream *stream = ifmt_ctx->streams[videoStream];
	// Copy contexts
	const AVCodec *decoder = avcodec_find_decoder(stream->codecpar->codec_id);

	printf("format->name =  %d   \n", stream->codecpar->format);  // h264

	printf("decoder->name =  %s  \n", decoder->name);  // h264
	printf("decoder->AVCodecID =  %d  \n", decoder->id);  //AV_CODEC_ID_H264

	decode_ctx = avcodec_alloc_context3(decoder);

	if (!decode_ctx) {
		fprintf(stderr, "Could not allocate video codec context\n");
		exit(1);
	}

	ret = avcodec_parameters_to_context(decode_ctx, stream->codecpar);

	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR,
				"Failed to copy decoder parameters to input decoder context "
						"for stream\n");
		return ret;
	}

	ret = avcodec_open2(decode_ctx, decoder, NULL);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream \n");
		return ret;
	}

	int i = 0;
	pFrame = av_frame_alloc();
	while (av_read_frame(ifmt_ctx, packet) >= 0) {
		if (packet->stream_index == videoStream) {
			i++;
			again: ret = avcodec_send_packet(decode_ctx, packet);
			if (ret < 0) {
				if (ret == AVERROR(EAGAIN)) {
					av_log(NULL, AV_LOG_ERROR, "AVERROR(EAGAIN) \n");
				}

				av_log(NULL, AV_LOG_ERROR, "Decoding failed: %d \n", ret);
				break;
			}

			ret = avcodec_receive_frame(decode_ctx, pFrame);
			if (ret == AVERROR(EAGAIN)) {
				goto again;

			}
			if (ret != 0) {
				av_log(NULL, AV_LOG_ERROR,
						"avcodec_receive_frame: failed %d \n", ret);
				break;
			}
			fprintf(stdout, "we get a video frame  \n");
			saveAsYuv420p(pFrame, i);
			if (i == 1) {
				break;
			}
		}
	}

	end: av_frame_free(&pFrame);
	av_packet_free(&packet);
	avformat_close_input(&ifmt_ctx);
	return 0;
}

